Simplify rewinding infix operators
This commit is contained in:
parent
5f3a2b4747
commit
4fd68ec5d1
@ -101,6 +101,7 @@ typedef enum {
|
|||||||
EXPR_METHOD_CALL, /**< This expression is the parameter list of a method call; only used by @ref dot and @ref call */
|
EXPR_METHOD_CALL, /**< This expression is the parameter list of a method call; only used by @ref dot and @ref call */
|
||||||
} ExpressionType;
|
} ExpressionType;
|
||||||
|
|
||||||
|
struct RewindState;
|
||||||
/**
|
/**
|
||||||
* @brief Subexpression parser function.
|
* @brief Subexpression parser function.
|
||||||
*
|
*
|
||||||
@ -108,7 +109,8 @@ typedef enum {
|
|||||||
* parser functions. The argument passed is the @ref ExpressionType
|
* parser functions. The argument passed is the @ref ExpressionType
|
||||||
* to compile the expression as.
|
* to compile the expression as.
|
||||||
*/
|
*/
|
||||||
typedef void (*ParseFn)(int);
|
typedef void (*ParsePrefixFn)(int);
|
||||||
|
typedef void (*ParseInfixFn)(int, struct RewindState *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parse rule table entry.
|
* @brief Parse rule table entry.
|
||||||
@ -120,8 +122,8 @@ typedef struct {
|
|||||||
#ifndef KRK_NO_SCAN_TRACING
|
#ifndef KRK_NO_SCAN_TRACING
|
||||||
const char * name; /**< Stringified token name for error messages and debugging. */
|
const char * name; /**< Stringified token name for error messages and debugging. */
|
||||||
#endif
|
#endif
|
||||||
ParseFn prefix; /**< Parse function to call when this token appears at the start of an expression. */
|
ParsePrefixFn prefix; /**< Parse function to call when this token appears at the start of an expression. */
|
||||||
ParseFn infix; /**< Parse function to call when this token appears after an expression. */
|
ParseInfixFn infix; /**< Parse function to call when this token appears after an expression. */
|
||||||
Precedence precedence; /**< Precedence ordering for Pratt parsing, @ref Precedence */
|
Precedence precedence; /**< Precedence ordering for Pratt parsing, @ref Precedence */
|
||||||
} ParseRule;
|
} ParseRule;
|
||||||
|
|
||||||
@ -252,6 +254,17 @@ typedef struct ChunkRecorder {
|
|||||||
size_t constants; /**< Number of constants in the constants table */
|
size_t constants; /**< Number of constants in the constants table */
|
||||||
} ChunkRecorder;
|
} ChunkRecorder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compiler emit and parse state prior to this expression.
|
||||||
|
*
|
||||||
|
* Used to rewind the parser for ternary and comma expressions.
|
||||||
|
*/
|
||||||
|
typedef struct RewindState {
|
||||||
|
ChunkRecorder before; /**< Bytecode and constant table output offsets. */
|
||||||
|
KrkScanner oldScanner; /**< Scanner cursor state. */
|
||||||
|
Parser oldParser; /**< Previous/current tokens. */
|
||||||
|
} RewindState;
|
||||||
|
|
||||||
static Parser parser;
|
static Parser parser;
|
||||||
static Compiler * current = NULL;
|
static Compiler * current = NULL;
|
||||||
static ClassCompiler * currentClass = NULL;
|
static ClassCompiler * currentClass = NULL;
|
||||||
@ -371,11 +384,7 @@ static KrkToken decorator(size_t level, FunctionType type);
|
|||||||
static void complexAssignment(ChunkRecorder before, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
static void complexAssignment(ChunkRecorder before, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
||||||
static void complexAssignmentTargets(KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
static void complexAssignmentTargets(KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized);
|
||||||
static int invalidTarget(int exprType, const char * description);
|
static int invalidTarget(int exprType, const char * description);
|
||||||
static void call(int exprType);
|
static void call(int exprType, RewindState *rewind);
|
||||||
|
|
||||||
/* These are not the real parse functions. */
|
|
||||||
static void commaX(int exprType) { }
|
|
||||||
static void ternaryX(int exprType) { }
|
|
||||||
|
|
||||||
static void finishError(KrkToken * token) {
|
static void finishError(KrkToken * token) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@ -770,12 +779,12 @@ static void compareChained(int inner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compare(int exprType) {
|
static void compare(int exprType, RewindState *rewind) {
|
||||||
compareChained(0);
|
compareChained(0);
|
||||||
invalidTarget(exprType, "operator");
|
invalidTarget(exprType, "operator");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void binary(int exprType) {
|
static void binary(int exprType, RewindState *rewind) {
|
||||||
KrkTokenType operatorType = parser.previous.type;
|
KrkTokenType operatorType = parser.previous.type;
|
||||||
ParseRule * rule = getRule(operatorType);
|
ParseRule * rule = getRule(operatorType);
|
||||||
parsePrecedence((Precedence)(rule->precedence + 1));
|
parsePrecedence((Precedence)(rule->precedence + 1));
|
||||||
@ -901,7 +910,7 @@ static void sliceExpression(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void getitem(int exprType) {
|
static void getitem(int exprType, RewindState *rewind) {
|
||||||
|
|
||||||
sliceExpression();
|
sliceExpression();
|
||||||
|
|
||||||
@ -942,7 +951,7 @@ static void getitem(int exprType) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dot(int exprType) {
|
static void dot(int exprType, RewindState *rewind) {
|
||||||
if (match(TOKEN_LEFT_PAREN)) {
|
if (match(TOKEN_LEFT_PAREN)) {
|
||||||
startEatingWhitespace();
|
startEatingWhitespace();
|
||||||
size_t argCount = 0;
|
size_t argCount = 0;
|
||||||
@ -1031,7 +1040,7 @@ _dotDone:
|
|||||||
EMIT_OPERAND_OP(OP_DEL_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_DEL_PROPERTY, ind);
|
||||||
} else if (match(TOKEN_LEFT_PAREN)) {
|
} else if (match(TOKEN_LEFT_PAREN)) {
|
||||||
EMIT_OPERAND_OP(OP_GET_METHOD, ind);
|
EMIT_OPERAND_OP(OP_GET_METHOD, ind);
|
||||||
call(EXPR_METHOD_CALL);
|
call(EXPR_METHOD_CALL,NULL);
|
||||||
} else {
|
} else {
|
||||||
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
EMIT_OPERAND_OP(OP_GET_PROPERTY, ind);
|
||||||
}
|
}
|
||||||
@ -3139,8 +3148,8 @@ static void dict(int exprType) {
|
|||||||
consume(TOKEN_RIGHT_BRACE,"Expected '}' at end of dict expression.");
|
consume(TOKEN_RIGHT_BRACE,"Expected '}' at end of dict expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ternary(ChunkRecorder before, KrkScanner oldScanner, Parser oldParser) {
|
static void ternary(int exprType, RewindState *rewind) {
|
||||||
rewindChunk(currentChunk(), before);
|
rewindChunk(currentChunk(), rewind->before);
|
||||||
|
|
||||||
parsePrecedence(PREC_OR);
|
parsePrecedence(PREC_OR);
|
||||||
|
|
||||||
@ -3156,8 +3165,8 @@ static void ternary(ChunkRecorder before, KrkScanner oldScanner, Parser oldParse
|
|||||||
patchJump(thenJump);
|
patchJump(thenJump);
|
||||||
emitByte(OP_POP);
|
emitByte(OP_POP);
|
||||||
|
|
||||||
krk_rewindScanner(oldScanner);
|
krk_rewindScanner(rewind->oldScanner);
|
||||||
parser = oldParser;
|
parser = rewind->oldParser;
|
||||||
parsePrecedence(PREC_OR);
|
parsePrecedence(PREC_OR);
|
||||||
patchJump(elseJump);
|
patchJump(elseJump);
|
||||||
|
|
||||||
@ -3230,7 +3239,7 @@ static void complexAssignment(ChunkRecorder before, KrkScanner oldScanner, Parse
|
|||||||
parser = outParser;
|
parser = outParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void comma(int exprType, ChunkRecorder before, KrkScanner oldScanner, Parser oldParser) {
|
static void comma(int exprType, RewindState *rewind) {
|
||||||
size_t expressionCount = 1;
|
size_t expressionCount = 1;
|
||||||
do {
|
do {
|
||||||
if (!getRule(parser.current.type)->prefix) break;
|
if (!getRule(parser.current.type)->prefix) break;
|
||||||
@ -3241,11 +3250,11 @@ static void comma(int exprType, ChunkRecorder before, KrkScanner oldScanner, Par
|
|||||||
EMIT_OPERAND_OP(OP_TUPLE,expressionCount);
|
EMIT_OPERAND_OP(OP_TUPLE,expressionCount);
|
||||||
|
|
||||||
if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) {
|
if (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) {
|
||||||
complexAssignment(before, oldScanner, oldParser, expressionCount, 0);
|
complexAssignment(rewind->before, rewind->oldScanner, rewind->oldParser, expressionCount, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void call(int exprType) {
|
static void call(int exprType, RewindState *rewind) {
|
||||||
startEatingWhitespace();
|
startEatingWhitespace();
|
||||||
size_t argCount = 0, specialArgs = 0, keywordArgs = 0, seenKeywordUnpacking = 0;
|
size_t argCount = 0, specialArgs = 0, keywordArgs = 0, seenKeywordUnpacking = 0;
|
||||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||||
@ -3346,14 +3355,14 @@ static void call(int exprType) {
|
|||||||
invalidTarget(exprType, "function call");
|
invalidTarget(exprType, "function call");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void and_(int exprType) {
|
static void and_(int exprType, RewindState *rewind) {
|
||||||
int endJump = emitJump(OP_JUMP_IF_FALSE_OR_POP);
|
int endJump = emitJump(OP_JUMP_IF_FALSE_OR_POP);
|
||||||
parsePrecedence(PREC_AND);
|
parsePrecedence(PREC_AND);
|
||||||
patchJump(endJump);
|
patchJump(endJump);
|
||||||
invalidTarget(exprType, "operator");
|
invalidTarget(exprType, "operator");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void or_(int exprType) {
|
static void or_(int exprType, RewindState *rewind) {
|
||||||
int endJump = emitJump(OP_JUMP_IF_TRUE_OR_POP);
|
int endJump = emitJump(OP_JUMP_IF_TRUE_OR_POP);
|
||||||
parsePrecedence(PREC_OR);
|
parsePrecedence(PREC_OR);
|
||||||
patchJump(endJump);
|
patchJump(endJump);
|
||||||
@ -3361,12 +3370,10 @@ static void or_(int exprType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void parsePrecedence(Precedence precedence) {
|
static void parsePrecedence(Precedence precedence) {
|
||||||
ChunkRecorder before = recordChunk(currentChunk());
|
RewindState rewind = {recordChunk(currentChunk()), krk_tellScanner(), parser};
|
||||||
KrkScanner oldScanner = krk_tellScanner();
|
|
||||||
Parser oldParser = parser;
|
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
|
ParsePrefixFn prefixRule = getRule(parser.previous.type)->prefix;
|
||||||
if (prefixRule == NULL) {
|
if (prefixRule == NULL) {
|
||||||
switch (parser.previous.type) {
|
switch (parser.previous.type) {
|
||||||
case TOKEN_RIGHT_BRACE:
|
case TOKEN_RIGHT_BRACE:
|
||||||
@ -3402,14 +3409,8 @@ static void parsePrecedence(Precedence precedence) {
|
|||||||
if (exprType == EXPR_ASSIGN_TARGET && (parser.previous.type == TOKEN_COMMA ||
|
if (exprType == EXPR_ASSIGN_TARGET && (parser.previous.type == TOKEN_COMMA ||
|
||||||
parser.previous.type == TOKEN_EQUAL)) break;
|
parser.previous.type == TOKEN_EQUAL)) break;
|
||||||
advance();
|
advance();
|
||||||
ParseFn infixRule = getRule(parser.previous.type)->infix;
|
ParseInfixFn infixRule = getRule(parser.previous.type)->infix;
|
||||||
if (infixRule == ternaryX) {
|
infixRule(exprType, &rewind);
|
||||||
ternary(before, oldScanner, oldParser);
|
|
||||||
} else if (infixRule == commaX) {
|
|
||||||
comma(exprType, before, oldScanner, oldParser);
|
|
||||||
} else {
|
|
||||||
infixRule(exprType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exprType == EXPR_CAN_ASSIGN && matchAssignment()) {
|
if (exprType == EXPR_CAN_ASSIGN && matchAssignment()) {
|
||||||
@ -3609,11 +3610,8 @@ ParseRule krk_parseRules[] = {
|
|||||||
RULE(TOKEN_RAISE, NULL, NULL, PREC_NONE),
|
RULE(TOKEN_RAISE, NULL, NULL, PREC_NONE),
|
||||||
RULE(TOKEN_ASYNC, NULL, NULL, PREC_NONE),
|
RULE(TOKEN_ASYNC, NULL, NULL, PREC_NONE),
|
||||||
|
|
||||||
/* These rules are special; their infix functions are not
|
RULE(TOKEN_COMMA, NULL, comma, PREC_COMMA),
|
||||||
* actually used by the parser; instead we have additional
|
RULE(TOKEN_IF, NULL, ternary, PREC_TERNARY),
|
||||||
* logic to enforce rewinding. */
|
|
||||||
RULE(TOKEN_COMMA, NULL, commaX, PREC_COMMA),
|
|
||||||
RULE(TOKEN_IF, NULL, ternaryX, PREC_TERNARY),
|
|
||||||
|
|
||||||
RULE(TOKEN_INDENTATION, NULL, NULL, PREC_NONE),
|
RULE(TOKEN_INDENTATION, NULL, NULL, PREC_NONE),
|
||||||
RULE(TOKEN_ERROR, NULL, NULL, PREC_NONE),
|
RULE(TOKEN_ERROR, NULL, NULL, PREC_NONE),
|
||||||
|
Loading…
Reference in New Issue
Block a user