Format spec support in f-strings

This commit is contained in:
K. Lange 2022-07-10 16:11:12 +09:00
parent 9230d4fee1
commit 7d409ebcbb
7 changed files with 106 additions and 13 deletions

View File

@ -2724,43 +2724,46 @@ static void string(int exprType) {
krk_rewindScanner(beforeExpression); /* To get us back to where we were with a string token */
parser = parserBefore;
c = inner.start;
KrkToken which = syntheticToken("str");
int hasEq = 0;
int formatType = 0;
while (*c == ' ') c++;
if (*c == '=') {
c++;
while (*c == ' ') c++;
emitConstant(OBJECT_VAL(krk_copyString(start,c-start)));
emitByte(OP_SWAP);
hasEq = 1;
formatType |= FORMAT_OP_EQ;
}
if (*c == '!') {
c++;
/* Conversion specifiers, must only be one */
if (*c == 'r') {
which = syntheticToken("repr");
formatType |= FORMAT_OP_REPR;
} else if (*c == 's') {
which = syntheticToken("str");
formatType |= FORMAT_OP_STR;
} else {
error("Unsupported conversion flag '%c' for f-string expression.", *c);
goto _cleanupError;
}
c++;
}
size_t ind = identifierConstant(&which);
EMIT_OPERAND_OP(OP_GET_GLOBAL, ind);
emitByte(OP_SWAP);
emitBytes(OP_CALL, 1);
if (*c == ':') {
/* TODO format specs */
error("Format spec not supported in f-string (GH-10)");
goto _cleanupError;
const char * formatStart = c+1;
c++;
while (c < end && *c != '}') c++;
emitConstant(OBJECT_VAL(krk_copyString(formatStart,c-formatStart)));
formatType |= FORMAT_OP_FORMAT;
}
EMIT_OPERAND_OP(OP_FORMAT_VALUE, formatType);
if (*c != '}') {
error("Expected closing '}' after expression in f-string");
goto _cleanupError;
}
if (hasEq) emitByte(OP_ADD);
if (atLeastOne) emitByte(OP_ADD);
atLeastOne = 1;
c++;

View File

@ -126,6 +126,7 @@ typedef enum {
OP_CALL_METHOD,
OP_CLOSE_MANY,
OP_POP_MANY,
OP_FORMAT_VALUE,
/* Two opcode instructions */
OP_JUMP_IF_FALSE_OR_POP,
@ -177,6 +178,7 @@ typedef enum {
OP_CALL_METHOD_LONG,
OP_CLOSE_MANY_LONG,
OP_POP_MANY_LONG,
OP_FORMAT_VALUE_LONG, /* should be unused */
} KrkOpCode;
/**

View File

@ -97,6 +97,7 @@ OPERAND(OP_SLICE, (void)0)
OPERAND(OP_CALL_METHOD, (void)0)
OPERAND(OP_CLOSE_MANY, (void)0)
OPERAND(OP_POP_MANY, (void)0)
OPERAND(OP_FORMAT_VALUE, (void)0)
JUMP(OP_JUMP_IF_FALSE_OR_POP,+)
JUMP(OP_JUMP_IF_TRUE_OR_POP,+)
JUMP(OP_JUMP,+)

View File

@ -51,3 +51,10 @@ typedef enum {
METHOD__MAX,
} KrkSpecialMethods;
#define FORMAT_OP_EQ (1 << 0)
#define FORMAT_OP_REPR (1 << 1)
#define FORMAT_OP_STR (1 << 2)
#define FORMAT_OP_FORMAT (1 << 3)

View File

@ -2537,6 +2537,74 @@ static inline void makeCollection(NativeFn func, size_t count) {
}
}
static inline int doFormatString(int options) {
if (options & FORMAT_OP_FORMAT) {
krk_swap(1);
if (options & FORMAT_OP_EQ) {
krk_swap(2);
}
} else if (options & FORMAT_OP_EQ) {
krk_swap(1);
}
/* Was this a repr or str call? (it can't be both) */
if (options & FORMAT_OP_STR) {
KrkClass * type = krk_getType(krk_peek(0));
if (likely(type->_tostr != NULL)) {
krk_push(krk_callDirect(type->_tostr, 1));
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return 1;
} else {
krk_runtimeError(vm.exceptions->typeError,
"Can not convert %s to str", krk_typeName(krk_peek(0)));
return 1;
}
} else if (options & FORMAT_OP_REPR) {
KrkClass * type = krk_getType(krk_peek(0));
if (likely(type->_reprer != NULL)) {
krk_push(krk_callDirect(type->_reprer, 1));
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return 1;
} else {
krk_runtimeError(vm.exceptions->typeError,
"Can not repr %s", krk_typeName(krk_peek(0)));
return 1;
}
}
if (!(options & FORMAT_OP_FORMAT)) {
/* Push empty string */
krk_push(OBJECT_VAL(S("")));
} else {
/* Swap args so value is first */
krk_swap(1);
}
/* Get the type of the value */
KrkClass * type = krk_getType(krk_peek(1));
if (likely(type->_format != NULL)) {
krk_push(krk_callDirect(type->_format, 2));
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return 1;
} else {
krk_runtimeError(vm.exceptions->attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(1)), "__format__");
return 1;
}
if (!IS_STRING(krk_peek(0))) {
krk_runtimeError(vm.exceptions->typeError, "format result is not str");
return 1;
}
if (options & FORMAT_OP_EQ) {
krk_push(krk_operator_add(krk_peek(1), krk_peek(0)));
krk_swap(2);
krk_pop();
krk_pop();
}
return 0;
}
/**
* VM main loop.
*/
@ -3429,6 +3497,14 @@ _finishReturn: (void)0;
krk_currentThread.stackTop[-count] = values->values.values[0];
break;
}
case OP_FORMAT_VALUE_LONG:
THREE_BYTE_OPERAND;
case OP_FORMAT_VALUE: {
ONE_BYTE_OPERAND;
if (doFormatString(OPERAND)) goto _finishException;
break;
}
}
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
_finishException:

2
test/testFormatSpecs.krk Normal file
View File

@ -0,0 +1,2 @@
print(f'{4225325:#016_x}')
print(f'{4225325325325325325632632532432432:#016_x}')

View File

@ -0,0 +1,2 @@
0x0000_0040_792d
0xd053_1a76_d6a2_ecdd_ed78_bd72_f630