Implement numeric string escapes and fix hexadecimal escape parsing and respect constant name length in undefined constant diagnostics
Build / AerScript (push) Successful in 39s

This commit is contained in:
2026-10-26 23:09:29 +01:00
parent ab323701d0
commit f74af13f98
2 changed files with 64 additions and 38 deletions
+61 -37
View File
@@ -554,12 +554,15 @@ static ph7_value *PH7_GenStateNewStrObj(ph7_gen_state *pGen, sxi32 *pCount) {
* \t horizontal tab (HT or 0x09 (9) in ASCII) * \t horizontal tab (HT or 0x09 (9) in ASCII)
* \v vertical tab (VT or 0x0B (11) in ASCII) * \v vertical tab (VT or 0x0B (11) in ASCII)
* \f form feed (FF or 0x0C (12) 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 * \\ backslash
* \$ dollar sign * \$ dollar sign
* \" double-quote * \" double-quote
* \[0-7]{1,3} the sequence of characters matching the regular expression is a character in octal notation * \[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 * \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. * The most important feature of double-quoted strings is the fact that variable names will be expanded.
* See string parsing for details. * See string parsing for details.
*/ */
@@ -603,7 +606,6 @@ static sxi32 PH7_GenStateCompileString(ph7_gen_state *pGen) {
break; break;
} }
if(zIn[0] == '\\') { if(zIn[0] == '\\') {
const char *zPtr = 0;
sxu32 n; sxu32 n;
zIn++; zIn++;
if(zIn >= zEnd) { if(zIn >= zEnd) {
@@ -629,9 +631,9 @@ static sxi32 PH7_GenStateCompileString(ph7_gen_state *pGen) {
/* The "alert" character (BEL)[ctrl+g] ASCII code 7 */ /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
PH7_MemObjStringAppend(pObj, "\a", sizeof(char)); PH7_MemObjStringAppend(pObj, "\a", sizeof(char));
break; break;
case 'b': case 'e':
/* Backspace (BS)[ctrl+h] ASCII code 8 */ /* Escape (ESC) ASCII code 27 */
PH7_MemObjStringAppend(pObj, "\b", sizeof(char)); PH7_MemObjStringAppend(pObj, "\033", sizeof(char));
break; break;
case 'f': case 'f':
/* Form-feed (FF)[ctrl+l] ASCII code 12 */ /* 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)); PH7_MemObjStringAppend(pObj, "\"", sizeof(char));
break; break;
case '0': case '0':
/* NUL byte */ case '1':
PH7_MemObjStringAppend(pObj, "\0", sizeof(char)); case '2':
break; case '3':
case 'x': case '4':
if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1])) { case '5':
int c; case '6':
/* Hex digit */ case '7':
c = SyHexToint(zIn[1]) << 4; case 'd':
if(&zIn[2] < zEnd) { case 'b':
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 'o': case 'o':
if(&zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8) { case 'x':
/* Octal digit stream */ {
int c; const char *zPtr = zIn;
c = 0; sxu32 c = 0;
zIn++; sxi32 iBase = 8;
for(zPtr = zIn ; zPtr < &zIn[3 * sizeof(char)] ; zPtr++) { sxi32 bBraced = FALSE;
if(zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7) { 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; 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;
}
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++;
} }
if(c > 0) {
PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char)); PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
}
n = (sxu32)(zPtr - zIn); n = (sxu32)(zPtr - zIn);
} else {
/* Output literal character */
PH7_MemObjStringAppend(pObj, "o", sizeof(char));
} }
break; break;
default: default:
+3 -1
View File
@@ -3306,8 +3306,10 @@ Resume:
pTos->nIdx = SXU32_HIGH; pTos->nIdx = SXU32_HIGH;
break; break;
} else if(pInstr[2].iOp != PH7_OP_MEMBER && pInstr[2].iOp != PH7_OP_NEW && pInstr[2].iOp != PH7_OP_IS) { } 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, PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
"Call to undefined constant ‘%s’", SyBlobData(&pObj->sBlob)); "Call to undefined constant ‘%z’", &sName);
} }
} }
} }