feat: add logical OR operator with short-circuit evaluation and token support

Add TOKEN_PIPEPIPE token for '||' syntax, implement or() parsing function with CODE_OR bytecode, and add VM interpreter logic that short-circuits on truthy left operand. Update .gitignore to exclude *.xccheckout files, extend test coverage for falsy behavior in and.wren and if.wren, and add comprehensive or.wren test suite verifying short-circuit semantics and truthiness rules.
This commit is contained in:
Bob Nystrom
2013-11-20 02:24:58 +00:00
parent 06be5808da
commit 643a7c4f97
7 changed files with 96 additions and 2 deletions
+26 -1
View File
@@ -32,6 +32,7 @@ typedef enum
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_PIPE,
TOKEN_PIPEPIPE,
TOKEN_AMP,
TOKEN_AMPAMP,
TOKEN_BANG,
@@ -384,7 +385,18 @@ static void readRawToken(Parser* parser)
}
return;
case '|': makeToken(parser, TOKEN_PIPE); return;
case '|':
if (peekChar(parser) == '|')
{
nextChar(parser);
makeToken(parser, TOKEN_PIPEPIPE);
}
else
{
makeToken(parser, TOKEN_PIPE);
}
return;
case '&':
if (peekChar(parser) == '&')
{
@@ -512,6 +524,7 @@ static void nextToken(Parser* parser)
case TOKEN_PLUS:
case TOKEN_MINUS:
case TOKEN_PIPE:
case TOKEN_PIPEPIPE:
case TOKEN_AMP:
case TOKEN_AMPAMP:
case TOKEN_BANG:
@@ -1000,6 +1013,17 @@ void and(Compiler* compiler, int allowAssignment)
patchJump(compiler, jump);
}
void or(Compiler* compiler, int allowAssignment)
{
// Skip the right argument if the left is true.
emit(compiler, CODE_OR);
int jump = emit(compiler, 255);
parsePrecedence(compiler, 0, PREC_LOGIC);
patchJump(compiler, jump);
}
void infixOp(Compiler* compiler, int allowAssignment)
{
GrammarRule* rule = &rules[compiler->parser->previous.type];
@@ -1073,6 +1097,7 @@ GrammarRule rules[] =
/* TOKEN_PLUS */ INFIX_OPERATOR(PREC_TERM, "+ "),
/* TOKEN_MINUS */ OPERATOR(PREC_TERM, "- "),
/* TOKEN_PIPE */ UNUSED,
/* TOKEN_PIPEPIPE */ INFIX(PREC_LOGIC, or),
/* TOKEN_AMP */ UNUSED,
/* TOKEN_AMPAMP */ INFIX(PREC_LOGIC, and),
/* TOKEN_BANG */ PREFIX_OPERATOR("!"),
+27
View File
@@ -577,6 +577,14 @@ int dumpInstruction(VM* vm, ObjFn* fn, int i)
break;
}
case CODE_OR:
{
int offset = bytecode[i++];
printf("OR %d\n", offset);
printf("%04d | offset %d\n", i, offset);
break;
}
case CODE_IS:
printf("CODE_IS\n");
break;
@@ -908,6 +916,25 @@ Value interpret(VM* vm, ObjFn* fn)
break;
}
case CODE_OR:
{
int offset = READ_ARG();
Value condition = PEEK();
// False is the only falsey value.
if (IS_FALSE(condition))
{
// Discard the condition and evaluate the right hand side.
POP();
}
else
{
// Short-circuit the right hand side.
ip += offset;
}
break;
}
case CODE_IS:
{
Value classObj = POP();
+4
View File
@@ -83,6 +83,10 @@ typedef enum
// continue.
CODE_AND,
// If the top of the stack is non-false, jump [arg] forward. Otherwise, pop
// and continue.
CODE_OR,
// Pop [a] then [b] and push true if [b] is an instance of [a].
CODE_IS,