Logical "||" operator.

This commit is contained in:
Bob Nystrom
2013-11-19 18:24:58 -08:00
parent 4266facfda
commit fe48113054
7 changed files with 96 additions and 2 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ build/
# XCode user-specific stuff.
xcuserdata/
*.xccheckout

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("!"),

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();

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,

View File

@ -18,3 +18,9 @@ io.write(true) && // expect: true
// Swallow a trailing newline.
io.write(true &&
true) // expect: true
// Only false is falsy.
io.write(0 && true) // expect: true
io.write(null && true) // expect: true
io.write("" && true) // expect: true
io.write(false && true) // expect: false

View File

@ -44,3 +44,8 @@ if (true) class Foo {} // no error
// Definition in else arm.
if (false) null else var a = io.write("ok") // expect: ok
if (true) null else class Foo {} // no error
// Only false is falsy.
if (0) io.write(0) // expect: 0
if (null) io.write(null) // expect: null
if ("") io.write("empty") // expect: empty

26
test/or.wren Normal file
View File

@ -0,0 +1,26 @@
// Note: These tests implicitly depend on ints being truthy.
// Also rely on io.write() returning its argument.
// Return the first true argument.
io.write(1 || true) // expect: 1
io.write(false || 1) // expect: 1
io.write(false || false || true) // expect: true
// Return the last argument if all are false.
io.write(false || false) // expect: false
io.write(false || false || false) // expect: false
// Short-circuit at the first true argument.
io.write(false) || // expect: false
io.write(true) || // expect: true
io.write(true) // should not print
// Swallow a trailing newline.
io.write(true ||
true) // expect: true
// Only false is falsy.
io.write(0 || true) // expect: 0
io.write(null || true) // expect: null
io.write(("" || true) == "") // expect: true
io.write(false || true) // expect: true