diff --git a/engine/compiler.c b/engine/compiler.c index 71cb51e..b0c1763 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -554,12 +554,15 @@ static ph7_value *PH7_GenStateNewStrObj(ph7_gen_state *pGen, sxi32 *pCount) { * \t horizontal tab (HT or 0x09 (9) in ASCII) * \v vertical tab (VT or 0x0B (11) in ASCII) * \f form feed (FF or 0x0C (12) in ASCII) + * \e escape (ESC or 0x1B (27) in ASCII) + * \b backspace, unless followed by a braced binary value * \\ backslash * \$ dollar sign * \" double-quote * \[0-7]{1,3} the sequence of characters matching the regular expression is a character in octal notation * \x[0-9A-Fa-f]{1,2} the sequence of characters matching the regular expression is a character in hexadecimal notation - * As in single quoted strings, escaping any other character will result in the backslash being printed too. + * \d{...}, \b{...}, \o{...}, \x{...} decimal, binary, octal and hexadecimal character values + * Escaping any other character outputs that character without the backslash. * The most important feature of double-quoted strings is the fact that variable names will be expanded. * See string parsing for details. */ @@ -603,7 +606,6 @@ static sxi32 PH7_GenStateCompileString(ph7_gen_state *pGen) { break; } if(zIn[0] == '\\') { - const char *zPtr = 0; sxu32 n; zIn++; if(zIn >= zEnd) { @@ -629,9 +631,9 @@ static sxi32 PH7_GenStateCompileString(ph7_gen_state *pGen) { /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */ PH7_MemObjStringAppend(pObj, "\a", sizeof(char)); break; - case 'b': - /* Backspace (BS)[ctrl+h] ASCII code 8 */ - PH7_MemObjStringAppend(pObj, "\b", sizeof(char)); + case 'e': + /* Escape (ESC) ASCII code 27 */ + PH7_MemObjStringAppend(pObj, "\033", sizeof(char)); break; case 'f': /* Form-feed (FF)[ctrl+l] ASCII code 12 */ @@ -662,44 +664,66 @@ static sxi32 PH7_GenStateCompileString(ph7_gen_state *pGen) { PH7_MemObjStringAppend(pObj, "\"", sizeof(char)); break; case '0': - /* NUL byte */ - PH7_MemObjStringAppend(pObj, "\0", sizeof(char)); - break; - case 'x': - if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1])) { - int c; - /* Hex digit */ - c = SyHexToint(zIn[1]) << 4; - if(&zIn[2] < zEnd) { - c += SyHexToint(zIn[2]); - } - /* Output char */ - PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char)); - n += sizeof(char) * 2; - } else { - /* Output literal character */ - PH7_MemObjStringAppend(pObj, "x", sizeof(char)); - } - break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case 'd': + case 'b': case 'o': - if(&zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8) { - /* Octal digit stream */ - int c; - c = 0; - zIn++; - for(zPtr = zIn ; zPtr < &zIn[3 * sizeof(char)] ; zPtr++) { - if(zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7) { + case 'x': + { + const char *zPtr = zIn; + sxu32 c = 0; + sxi32 iBase = 8; + sxi32 bBraced = FALSE; + sxu32 nDigit = 0; + sxu32 nLimit = 3; + if(zIn[0] < '0' || zIn[0] > '7') { + bBraced = zEnd - zIn > 1 && zIn[1] == '{'; + if(!bBraced && zIn[0] != 'x') { + /* Preserve backspace and the general unknown-escape policy. */ + PH7_MemObjStringAppend(pObj, zIn[0] == 'b' ? "\b" : zIn, sizeof(char)); break; } - c = c * 8 + (zPtr[0] - '0'); + iBase = zIn[0] == 'x' ? 16 : zIn[0] == 'd' ? 10 : zIn[0] == 'b' ? 2 : 8; + zPtr += bBraced ? 2 : 1; + nLimit = 2; } - if(c > 0) { - PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char)); + while(zPtr < zEnd && (bBraced || nDigit < nLimit)) { + sxi32 iDigit; + if(bBraced && zPtr[0] == '}') { + break; + } + iDigit = (unsigned char)zPtr[0] < 0x80 && SyisHex(zPtr[0]) ? SyHexToint(zPtr[0]) : -1; + if(iDigit < 0 || iDigit >= iBase) { + if(bBraced) { + PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Invalid digit in \\%c{...} escape sequence", zIn[0]); + return SXERR_ABORT; + } + break; + } + /* Unsigned accumulation avoids signed overflow on long digit streams. */ + c = c * (sxu32)iBase + (sxu32)iDigit; + nDigit++; + zPtr++; } + if(nDigit == 0) { + PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Expected digit in \\%c escape sequence", zIn[0]); + return SXERR_ABORT; + } + if(bBraced) { + if(zPtr >= zEnd) { + PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Missing '}' in \\%c escape sequence", zIn[0]); + return SXERR_ABORT; + } + zPtr++; + } + PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char)); n = (sxu32)(zPtr - zIn); - } else { - /* Output literal character */ - PH7_MemObjStringAppend(pObj, "o", sizeof(char)); } break; default: diff --git a/engine/vm.c b/engine/vm.c index 5bd5c43..da9e220 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -3306,8 +3306,10 @@ Resume: pTos->nIdx = SXU32_HIGH; break; } else if(pInstr[2].iOp != PH7_OP_MEMBER && pInstr[2].iOp != PH7_OP_NEW && pInstr[2].iOp != PH7_OP_IS) { + SyString sName; + SyStringInitFromBuf(&sName, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, - "Call to undefined constant ā€˜%s’", SyBlobData(&pObj->sBlob)); + "Call to undefined constant ā€˜%z’", &sName); } } }