mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 14:18:42 +01:00
Compare commits
10 Commits
0.4.0
...
compound-a
| Author | SHA1 | Date | |
|---|---|---|---|
| c4497933e7 | |||
| 19548ec411 | |||
| 119fd7fd0f | |||
| 903f2eae07 | |||
| 4baef23d1d | |||
| 6c26f8aff5 | |||
| 234efa2e3a | |||
| 685f9b512d | |||
| 7267b23913 | |||
| a3f368d1ae |
@ -66,15 +66,25 @@ typedef enum
|
||||
TOKEN_STAR,
|
||||
TOKEN_SLASH,
|
||||
TOKEN_PERCENT,
|
||||
TOKEN_PERCENTEQ,
|
||||
TOKEN_PLUS,
|
||||
TOKEN_MINUS,
|
||||
TOKEN_PLUSEQ,
|
||||
TOKEN_MINUSEQ,
|
||||
TOKEN_STAREQ,
|
||||
TOKEN_SLASHEQ,
|
||||
TOKEN_LTLT,
|
||||
TOKEN_GTGT,
|
||||
TOKEN_LTLTEQ,
|
||||
TOKEN_GTGTEQ,
|
||||
TOKEN_PIPE,
|
||||
TOKEN_PIPEPIPE,
|
||||
TOKEN_PIPEEQ,
|
||||
TOKEN_CARET,
|
||||
TOKEN_CARETEQ,
|
||||
TOKEN_AMP,
|
||||
TOKEN_AMPAMP,
|
||||
TOKEN_AMPEQ,
|
||||
TOKEN_BANG,
|
||||
TOKEN_TILDE,
|
||||
TOKEN_QUESTION,
|
||||
@ -965,16 +975,34 @@ static void nextToken(Parser* parser)
|
||||
case '}': makeToken(parser, TOKEN_RIGHT_BRACE); return;
|
||||
case ':': makeToken(parser, TOKEN_COLON); return;
|
||||
case ',': makeToken(parser, TOKEN_COMMA); return;
|
||||
case '*': makeToken(parser, TOKEN_STAR); return;
|
||||
case '%': makeToken(parser, TOKEN_PERCENT); return;
|
||||
case '^': makeToken(parser, TOKEN_CARET); return;
|
||||
case '+': makeToken(parser, TOKEN_PLUS); return;
|
||||
case '-': makeToken(parser, TOKEN_MINUS); return;
|
||||
case '*': twoCharToken(parser, '=', TOKEN_STAREQ, TOKEN_STAR); return;
|
||||
case '%': twoCharToken(parser, '=', TOKEN_PERCENTEQ, TOKEN_PERCENT); return;
|
||||
case '^': twoCharToken(parser, '=', TOKEN_CARETEQ, TOKEN_CARET); return;
|
||||
case '+': twoCharToken(parser, '=', TOKEN_PLUSEQ, TOKEN_PLUS); return;
|
||||
case '-': twoCharToken(parser, '=', TOKEN_MINUSEQ, TOKEN_MINUS); return;
|
||||
case '~': makeToken(parser, TOKEN_TILDE); return;
|
||||
case '?': makeToken(parser, TOKEN_QUESTION); return;
|
||||
|
||||
case '|': twoCharToken(parser, '|', TOKEN_PIPEPIPE, TOKEN_PIPE); return;
|
||||
case '&': twoCharToken(parser, '&', TOKEN_AMPAMP, TOKEN_AMP); return;
|
||||
case '|':
|
||||
if (matchChar(parser, '|'))
|
||||
{
|
||||
makeToken(parser, TOKEN_PIPEPIPE);
|
||||
return;
|
||||
}
|
||||
|
||||
twoCharToken(parser, '=', TOKEN_PIPEEQ, TOKEN_PIPE);
|
||||
return;
|
||||
|
||||
case '&':
|
||||
if (matchChar(parser, '&'))
|
||||
{
|
||||
makeToken(parser, TOKEN_AMPAMP);
|
||||
return;
|
||||
}
|
||||
|
||||
twoCharToken(parser, '=', TOKEN_AMPEQ, TOKEN_AMP);
|
||||
return;
|
||||
|
||||
case '=': twoCharToken(parser, '=', TOKEN_EQEQ, TOKEN_EQ); return;
|
||||
case '!': twoCharToken(parser, '=', TOKEN_BANGEQ, TOKEN_BANG); return;
|
||||
|
||||
@ -1001,13 +1029,13 @@ static void nextToken(Parser* parser)
|
||||
break;
|
||||
}
|
||||
|
||||
makeToken(parser, TOKEN_SLASH);
|
||||
twoCharToken(parser, '=', TOKEN_SLASHEQ, TOKEN_SLASH);
|
||||
return;
|
||||
|
||||
case '<':
|
||||
if (matchChar(parser, '<'))
|
||||
{
|
||||
makeToken(parser, TOKEN_LTLT);
|
||||
twoCharToken(parser, '=', TOKEN_LTLTEQ, TOKEN_LTLT);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1018,7 +1046,7 @@ static void nextToken(Parser* parser)
|
||||
case '>':
|
||||
if (matchChar(parser, '>'))
|
||||
{
|
||||
makeToken(parser, TOKEN_GTGT);
|
||||
twoCharToken(parser, '=', TOKEN_GTGTEQ, TOKEN_GTGT);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1169,6 +1197,12 @@ static int emitByte(Compiler* compiler, int byte)
|
||||
return compiler->fn->code.count - 1;
|
||||
}
|
||||
|
||||
static void emitPush(Compiler* compiler, int count)
|
||||
{
|
||||
emitByte(compiler, CODE_PUSH);
|
||||
emitByte(compiler, count);
|
||||
}
|
||||
|
||||
// Emits one bytecode instruction.
|
||||
static void emitOp(Compiler* compiler, Code instruction)
|
||||
{
|
||||
@ -1591,6 +1625,31 @@ static void expression(Compiler* compiler);
|
||||
static void statement(Compiler* compiler);
|
||||
static void definition(Compiler* compiler);
|
||||
static void parsePrecedence(Compiler* compiler, Precedence precedence);
|
||||
//Compound assignment forward declares and helpers
|
||||
static void fieldAssignment(Compiler* compiler, int field, bool insideMethod);
|
||||
static void variableAssignment(Compiler* compiler, Variable variable);
|
||||
static void setterAssignment(Compiler* compiler, Code instruction, Signature signature);
|
||||
static void subscriptSetterAssignment(Compiler* compiler, Signature signature);
|
||||
static inline bool isAssignment(Compiler* compiler)
|
||||
{
|
||||
TokenType next = peek(compiler);
|
||||
switch(next) {
|
||||
case TOKEN_EQ:
|
||||
case TOKEN_PLUSEQ:
|
||||
case TOKEN_MINUSEQ:
|
||||
case TOKEN_STAREQ:
|
||||
case TOKEN_SLASHEQ:
|
||||
case TOKEN_LTLTEQ:
|
||||
case TOKEN_GTGTEQ:
|
||||
case TOKEN_PERCENTEQ:
|
||||
case TOKEN_CARETEQ:
|
||||
case TOKEN_PIPEEQ:
|
||||
case TOKEN_AMPEQ:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Replaces the placeholder argument for a previous CODE_JUMP or CODE_JUMP_IF
|
||||
// instruction with an offset that jumps to the current end of bytecode.
|
||||
@ -1919,16 +1978,17 @@ static void namedCall(Compiler* compiler, bool canAssign, Code instruction)
|
||||
// Get the token for the method name.
|
||||
Signature signature = signatureFromToken(compiler, SIG_GETTER);
|
||||
|
||||
if (canAssign && match(compiler, TOKEN_EQ))
|
||||
if (canAssign && isAssignment(compiler))
|
||||
{
|
||||
ignoreNewlines(compiler);
|
||||
|
||||
// Compile the assignment and right-hand side.
|
||||
setterAssignment(compiler, instruction, signature);
|
||||
|
||||
// Build the setter signature.
|
||||
signature.type = SIG_SETTER;
|
||||
signature.arity = 1;
|
||||
|
||||
// Compile the assigned value.
|
||||
expression(compiler);
|
||||
callSignature(compiler, instruction, &signature);
|
||||
}
|
||||
else
|
||||
@ -2109,18 +2169,20 @@ static void field(Compiler* compiler, bool canAssign)
|
||||
}
|
||||
}
|
||||
|
||||
bool insideMethod = compiler->parent != NULL &&
|
||||
compiler->parent->enclosingClass == enclosingClass;
|
||||
|
||||
// If there's an "=" after a field name, it's an assignment.
|
||||
bool isLoad = true;
|
||||
if (canAssign && match(compiler, TOKEN_EQ))
|
||||
if (canAssign && isAssignment(compiler))
|
||||
{
|
||||
// Compile the right-hand side.
|
||||
expression(compiler);
|
||||
// Compile the assignment and right-hand side.
|
||||
fieldAssignment(compiler, field, insideMethod);
|
||||
isLoad = false;
|
||||
}
|
||||
|
||||
// If we're directly inside a method, use a more optimal instruction.
|
||||
if (compiler->parent != NULL &&
|
||||
compiler->parent->enclosingClass == enclosingClass)
|
||||
if (insideMethod)
|
||||
{
|
||||
emitByteArg(compiler, isLoad ? CODE_LOAD_FIELD_THIS : CODE_STORE_FIELD_THIS,
|
||||
field);
|
||||
@ -2135,11 +2197,11 @@ static void field(Compiler* compiler, bool canAssign)
|
||||
// Compiles a read or assignment to [variable].
|
||||
static void bareName(Compiler* compiler, bool canAssign, Variable variable)
|
||||
{
|
||||
// If there's an "=" after a bare name, it's a variable assignment.
|
||||
if (canAssign && match(compiler, TOKEN_EQ))
|
||||
// If there's an assignment after a bare name, it's a variable assignment.
|
||||
if (canAssign && isAssignment(compiler))
|
||||
{
|
||||
// Compile the right-hand side.
|
||||
expression(compiler);
|
||||
// Compile the assignment and right-hand side.
|
||||
variableAssignment(compiler, variable);
|
||||
|
||||
// Emit the store instruction.
|
||||
switch (variable.scope)
|
||||
@ -2358,13 +2420,14 @@ static void subscript(Compiler* compiler, bool canAssign)
|
||||
finishArgumentList(compiler, &signature);
|
||||
consume(compiler, TOKEN_RIGHT_BRACKET, "Expect ']' after arguments.");
|
||||
|
||||
if (canAssign && match(compiler, TOKEN_EQ))
|
||||
if (canAssign && isAssignment(compiler))
|
||||
{
|
||||
signature.type = SIG_SUBSCRIPT_SETTER;
|
||||
// Compile the assignment and right-hand side.
|
||||
validateNumParameters(compiler, signature.arity + 1);
|
||||
subscriptSetterAssignment(compiler, signature);
|
||||
|
||||
// Compile the assigned value.
|
||||
validateNumParameters(compiler, ++signature.arity);
|
||||
expression(compiler);
|
||||
signature.arity++;
|
||||
signature.type = SIG_SUBSCRIPT_SETTER;
|
||||
}
|
||||
|
||||
callSignature(compiler, CODE_CALL_0, &signature);
|
||||
@ -2424,10 +2487,8 @@ static void conditional(Compiler* compiler, bool canAssign)
|
||||
patchJump(compiler, elseJump);
|
||||
}
|
||||
|
||||
void infixOp(Compiler* compiler, bool canAssign)
|
||||
void infixOpWithRule(Compiler* compiler, GrammarRule* rule)
|
||||
{
|
||||
GrammarRule* rule = getRule(compiler->parser->previous.type);
|
||||
|
||||
// An infix operator cannot end an expression.
|
||||
ignoreNewlines(compiler);
|
||||
|
||||
@ -2439,6 +2500,13 @@ void infixOp(Compiler* compiler, bool canAssign)
|
||||
callSignature(compiler, CODE_CALL_0, &signature);
|
||||
}
|
||||
|
||||
void infixOp(Compiler* compiler, bool canAssign)
|
||||
{
|
||||
GrammarRule* rule = getRule(compiler->parser->previous.type);
|
||||
infixOpWithRule(compiler, rule);
|
||||
}
|
||||
|
||||
|
||||
// Compiles a method signature for an infix operator.
|
||||
void infixSignature(Compiler* compiler, Signature* signature)
|
||||
{
|
||||
@ -2603,15 +2671,25 @@ GrammarRule rules[] =
|
||||
/* TOKEN_STAR */ INFIX_OPERATOR(PREC_FACTOR, "*"),
|
||||
/* TOKEN_SLASH */ INFIX_OPERATOR(PREC_FACTOR, "/"),
|
||||
/* TOKEN_PERCENT */ INFIX_OPERATOR(PREC_FACTOR, "%"),
|
||||
/* TOKEN_PERCENTEQ */ UNUSED,
|
||||
/* TOKEN_PLUS */ INFIX_OPERATOR(PREC_TERM, "+"),
|
||||
/* TOKEN_MINUS */ OPERATOR("-"),
|
||||
/* TOKEN_PLUSEQ */ UNUSED,
|
||||
/* TOKEN_MINUSEQ */ UNUSED,
|
||||
/* TOKEN_STAREQ */ UNUSED,
|
||||
/* TOKEN_SLASHEQ */ UNUSED,
|
||||
/* TOKEN_LTLT */ INFIX_OPERATOR(PREC_BITWISE_SHIFT, "<<"),
|
||||
/* TOKEN_GTGT */ INFIX_OPERATOR(PREC_BITWISE_SHIFT, ">>"),
|
||||
/* TOKEN_LTLTEQ */ UNUSED,
|
||||
/* TOKEN_GTGTEQ */ UNUSED,
|
||||
/* TOKEN_PIPE */ INFIX_OPERATOR(PREC_BITWISE_OR, "|"),
|
||||
/* TOKEN_PIPEPIPE */ INFIX(PREC_LOGICAL_OR, or_),
|
||||
/* TOKEN_PIPEEQ */ UNUSED,
|
||||
/* TOKEN_CARET */ INFIX_OPERATOR(PREC_BITWISE_XOR, "^"),
|
||||
/* TOKEN_CARETEQ */ UNUSED,
|
||||
/* TOKEN_AMP */ INFIX_OPERATOR(PREC_BITWISE_AND, "&"),
|
||||
/* TOKEN_AMPAMP */ INFIX(PREC_LOGICAL_AND, and_),
|
||||
/* TOKEN_AMPEQ */ UNUSED,
|
||||
/* TOKEN_BANG */ PREFIX_OPERATOR("!"),
|
||||
/* TOKEN_TILDE */ PREFIX_OPERATOR("~"),
|
||||
/* TOKEN_QUESTION */ INFIX(PREC_ASSIGNMENT, conditional),
|
||||
@ -2695,6 +2773,94 @@ void expression(Compiler* compiler)
|
||||
parsePrecedence(compiler, PREC_LOWEST);
|
||||
}
|
||||
|
||||
//Compound assigment handling
|
||||
TokenType compoundAssignmentStart(Compiler* compiler)
|
||||
{
|
||||
//Consume the compound assign token
|
||||
nextToken(compiler->parser);
|
||||
switch(compiler->parser->previous.type)
|
||||
{
|
||||
//For the case of a normal = we handle the RHS
|
||||
case TOKEN_EQ: {
|
||||
expression(compiler);
|
||||
return TOKEN_EQ;
|
||||
} break;
|
||||
|
||||
case TOKEN_PLUSEQ: return TOKEN_PLUS;
|
||||
case TOKEN_MINUSEQ: return TOKEN_MINUS;
|
||||
case TOKEN_STAREQ: return TOKEN_STAR;
|
||||
case TOKEN_SLASHEQ: return TOKEN_SLASH;
|
||||
case TOKEN_LTLTEQ: return TOKEN_LTLT;
|
||||
case TOKEN_GTGTEQ: return TOKEN_GTGT;
|
||||
case TOKEN_PERCENTEQ: return TOKEN_PERCENT;
|
||||
case TOKEN_PIPEEQ: return TOKEN_PIPE;
|
||||
case TOKEN_AMPEQ: return TOKEN_AMP;
|
||||
case TOKEN_CARETEQ: return TOKEN_CARET;
|
||||
default: break;
|
||||
};
|
||||
|
||||
UNREACHABLE();
|
||||
return TOKEN_ERROR;
|
||||
}
|
||||
|
||||
static void variableAssignment(Compiler* compiler, Variable variable)
|
||||
{
|
||||
TokenType infixToken = compoundAssignmentStart(compiler);
|
||||
if(infixToken == TOKEN_EQ) return;
|
||||
|
||||
//We're doing somevar = somevar + ...
|
||||
//so we load the variable again as the arg to the infix
|
||||
loadVariable(compiler, variable);
|
||||
infixOpWithRule(compiler, getRule(infixToken));
|
||||
}
|
||||
|
||||
static void fieldAssignment(Compiler* compiler, int field, bool insideMethod)
|
||||
{
|
||||
TokenType infixToken = compoundAssignmentStart(compiler);
|
||||
if(infixToken == TOKEN_EQ) return;
|
||||
|
||||
//We're doing _field = _field + ...
|
||||
//so we load the field as the arg for the infix
|
||||
if(insideMethod) {
|
||||
emitByteArg(compiler, CODE_LOAD_FIELD_THIS, field);
|
||||
} else {
|
||||
loadThis(compiler);
|
||||
emitByteArg(compiler, CODE_LOAD_FIELD, field);
|
||||
}
|
||||
//Then call the op which handles the RHS
|
||||
infixOpWithRule(compiler, getRule(infixToken));
|
||||
}
|
||||
|
||||
static void setterAssignment(Compiler* compiler, Code instruction, Signature signature)
|
||||
{
|
||||
TokenType infixToken = compoundAssignmentStart(compiler);
|
||||
if(infixToken == TOKEN_EQ) return;
|
||||
|
||||
//We're doing self.setter = self.getter + ...
|
||||
//so to call the getter, we duplicate the self on the stack
|
||||
emitPush(compiler, 1);
|
||||
callSignature(compiler, instruction, &signature);
|
||||
//Then call the op which handles the RHS
|
||||
infixOpWithRule(compiler, getRule(infixToken));
|
||||
|
||||
}
|
||||
|
||||
static void subscriptSetterAssignment(Compiler* compiler, Signature signature)
|
||||
{
|
||||
TokenType infixToken = compoundAssignmentStart(compiler);
|
||||
if(infixToken == TOKEN_EQ) return;
|
||||
|
||||
//We're doing self[_,_] = self[_,_] + ...
|
||||
//so to call the getter, we duplicate the
|
||||
//subscript self + args on the stack
|
||||
emitPush(compiler, signature.arity + 1);
|
||||
callSignature(compiler, CODE_CALL_0, &signature);
|
||||
//Then call the op which handles the RHS
|
||||
infixOpWithRule(compiler, getRule(infixToken));
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of arguments to the instruction at [ip] in [fn]'s
|
||||
// bytecode.
|
||||
static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
@ -2734,6 +2900,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_LOAD_FIELD:
|
||||
case CODE_STORE_FIELD:
|
||||
case CODE_CLASS:
|
||||
case CODE_PUSH:
|
||||
return 1;
|
||||
|
||||
case CODE_CONSTANT:
|
||||
|
||||
@ -177,6 +177,11 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
case CODE_STORE_FIELD: BYTE_INSTRUCTION("STORE_FIELD");
|
||||
|
||||
case CODE_POP: printf("POP\n"); break;
|
||||
case CODE_PUSH: {
|
||||
int count = READ_BYTE();
|
||||
printf("PUSH %d\n", count);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_CALL_0:
|
||||
case CODE_CALL_1:
|
||||
|
||||
@ -77,6 +77,9 @@ OPCODE(STORE_FIELD, -1)
|
||||
// Pop and discard the top of stack.
|
||||
OPCODE(POP, -1)
|
||||
|
||||
// Push N values to the stack, from the stack. The number is unused.
|
||||
OPCODE(PUSH, 0)
|
||||
|
||||
// Invoke the method with symbol [arg]. The number indicates the number of
|
||||
// arguments (not including the receiver).
|
||||
OPCODE(CALL_0, 0)
|
||||
|
||||
@ -1146,6 +1146,19 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
DROP();
|
||||
DISPATCH();
|
||||
|
||||
CASE_CODE(PUSH):
|
||||
{
|
||||
uint8_t count = READ_BYTE();
|
||||
wrenEnsureStack(vm, fiber, fiber->stackCapacity + count);
|
||||
Value* stackTopBefore = fiber->stackTop;
|
||||
for(uint8_t i = count; i > 0; --i)
|
||||
{
|
||||
Value* value = stackTopBefore - i;
|
||||
PUSH(*value);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
CASE_CODE(RETURN):
|
||||
{
|
||||
Value result = POP();
|
||||
|
||||
40
test/language/assignment/compound_field.wren
Normal file
40
test/language/assignment/compound_field.wren
Normal file
@ -0,0 +1,40 @@
|
||||
class Compound {
|
||||
value { _value }
|
||||
construct new() {
|
||||
_value = 0
|
||||
}
|
||||
|
||||
plus() {
|
||||
_value += 5
|
||||
}
|
||||
minus() {
|
||||
_value -= 1
|
||||
}
|
||||
star() {
|
||||
_value *= 4
|
||||
}
|
||||
slash() {
|
||||
_value /= 2
|
||||
}
|
||||
ltlt() {
|
||||
_value <<= 2
|
||||
}
|
||||
gtgt() {
|
||||
_value >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
var a = Compound.new()
|
||||
System.print(a.value) // expect: 0
|
||||
a.plus()
|
||||
System.print(a.value) // expect: 5
|
||||
a.minus()
|
||||
System.print(a.value) // expect: 4
|
||||
a.star()
|
||||
System.print(a.value) // expect: 16
|
||||
a.slash()
|
||||
System.print(a.value) // expect: 8
|
||||
a.ltlt()
|
||||
System.print(a.value) // expect: 32
|
||||
a.gtgt()
|
||||
System.print(a.value) // expect: 16
|
||||
57
test/language/assignment/compound_setter.wren
Normal file
57
test/language/assignment/compound_setter.wren
Normal file
@ -0,0 +1,57 @@
|
||||
class Compound {
|
||||
value { _value }
|
||||
value=(v) { _value = v }
|
||||
other { _other }
|
||||
other=(v) { _other = v }
|
||||
construct new() {
|
||||
_value = 0
|
||||
}
|
||||
}
|
||||
|
||||
var a = Compound.new()
|
||||
a.value = 0
|
||||
System.print(a.value) // expect: 0
|
||||
a.value += 5
|
||||
System.print(a.value) // expect: 5
|
||||
a.value -= 1
|
||||
System.print(a.value) // expect: 4
|
||||
a.value *= 4
|
||||
System.print(a.value) // expect: 16
|
||||
a.value /= 2
|
||||
System.print(a.value) // expect: 8
|
||||
a.value <<= 1
|
||||
System.print(a.value) // expect: 16
|
||||
a.value >>= 2
|
||||
System.print(a.value) // expect: 4
|
||||
|
||||
a.other = Compound.new()
|
||||
a.other.value = 4
|
||||
System.print(a.other.value) // expect: 4
|
||||
a.other.value += 10
|
||||
System.print(a.other.value) // expect: 14
|
||||
a.other.value -= 2
|
||||
System.print(a.other.value) // expect: 12
|
||||
a.other.value *= 2
|
||||
System.print(a.other.value) // expect: 24
|
||||
a.other.value /= 6
|
||||
System.print(a.other.value) // expect: 4
|
||||
a.other.value <<= 4
|
||||
System.print(a.other.value) // expect: 64
|
||||
a.other.value >>= 3
|
||||
System.print(a.other.value) // expect: 8
|
||||
|
||||
a.other.other = Compound.new()
|
||||
a.other.other.value = 2
|
||||
System.print(a.other.other.value) // expect: 2
|
||||
a.other.other.value += 8
|
||||
System.print(a.other.other.value) // expect: 10
|
||||
a.other.other.value *= 10
|
||||
System.print(a.other.other.value) // expect: 100
|
||||
a.other.other.value -= 1
|
||||
System.print(a.other.other.value) // expect: 99
|
||||
a.other.other.value /= 3
|
||||
System.print(a.other.other.value) // expect: 33
|
||||
a.other.other.value <<= 1
|
||||
System.print(a.other.other.value) // expect: 66
|
||||
a.other.other.value >>= 2
|
||||
System.print(a.other.other.value) // expect: 16
|
||||
67
test/language/assignment/compound_subscript.wren
Normal file
67
test/language/assignment/compound_subscript.wren
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
class Compound {
|
||||
value { _value }
|
||||
value=(v) { _value = v }
|
||||
other { _other }
|
||||
other=(v) { _other = v }
|
||||
|
||||
[index] { _value }
|
||||
[index]=(v) { _value = v }
|
||||
|
||||
[index, other, last] { _value }
|
||||
[index, other, last]=(v) { _value = v }
|
||||
|
||||
construct new() {
|
||||
_value = 0
|
||||
}
|
||||
}
|
||||
|
||||
var a = Compound.new()
|
||||
a[0] = 0
|
||||
System.print(a[0]) // expect: 0
|
||||
a[0] += 5
|
||||
System.print(a[0]) // expect: 5
|
||||
a[0] -= 1
|
||||
System.print(a[0]) // expect: 4
|
||||
a[0] *= 4
|
||||
System.print(a[0]) // expect: 16
|
||||
a[0] /= 2
|
||||
System.print(a[0]) // expect: 8
|
||||
a[0] <<= 2
|
||||
System.print(a[0]) // expect: 32
|
||||
a[0] >>= 4
|
||||
System.print(a[0]) // expect: 2
|
||||
|
||||
a.other = Compound.new()
|
||||
a.other[0] = 4
|
||||
System.print(a.other[0]) // expect: 4
|
||||
a.other[0] += 10
|
||||
System.print(a.other[0]) // expect: 14
|
||||
a.other[0] -= 2
|
||||
System.print(a.other[0]) // expect: 12
|
||||
a.other[0] *= 2
|
||||
System.print(a.other[0]) // expect: 24
|
||||
a.other[0] /= 6
|
||||
System.print(a.other[0]) // expect: 4
|
||||
a.other[0] <<= 5
|
||||
System.print(a.other[0]) // expect: 128
|
||||
a.other[0] >>= 3
|
||||
System.print(a.other[0]) // expect: 16
|
||||
|
||||
a.other.other = Compound.new()
|
||||
a.other.other[1] = 2
|
||||
System.print(a.other.other[1]) // expect: 2
|
||||
a.other.other[1] += 8
|
||||
System.print(a.other.other[1]) // expect: 10
|
||||
a.other.other[1] *= 10
|
||||
System.print(a.other.other[1]) // expect: 100
|
||||
a.other.other[1] -= 1
|
||||
System.print(a.other.other[1]) // expect: 99
|
||||
a.other.other[1] /= 3
|
||||
System.print(a.other.other[1]) // expect: 33
|
||||
a.other.other[1] <<= 1
|
||||
System.print(a.other.other[1]) // expect: 66
|
||||
a.other.other[1] >>= 3
|
||||
System.print(a.other.other[1]) // expect: 8
|
||||
|
||||
|
||||
20
test/language/assignment/compound_subscript_list.wren
Normal file
20
test/language/assignment/compound_subscript_list.wren
Normal file
@ -0,0 +1,20 @@
|
||||
var list = [1,2,3,4]
|
||||
|
||||
list[0] += 10
|
||||
System.print(list[0]) // expect: 11
|
||||
|
||||
list[1] -= 3
|
||||
System.print(list[1]) // expect: -1
|
||||
|
||||
list[2] *= 9
|
||||
System.print(list[2]) // expect: 27
|
||||
|
||||
list[3] /= 2
|
||||
System.print(list[3]) // expect: 2
|
||||
|
||||
list[0] <<= 2
|
||||
System.print(list[0]) // expect: 44
|
||||
|
||||
list[2] >>= 1
|
||||
System.print(list[2]) // expect: 13
|
||||
|
||||
64
test/language/assignment/compound_subscript_multi.wren
Normal file
64
test/language/assignment/compound_subscript_multi.wren
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
class Compound {
|
||||
value { _value }
|
||||
value=(v) { _value = v }
|
||||
other { _other }
|
||||
other=(v) { _other = v }
|
||||
|
||||
[index, other, last] { _value }
|
||||
[index, other, last]=(v) { _value = v }
|
||||
|
||||
construct new() {
|
||||
_value = 0
|
||||
}
|
||||
}
|
||||
|
||||
var a = Compound.new()
|
||||
a[0,1,2] = 0
|
||||
System.print(a[0,1,2]) // expect: 0
|
||||
a[0,1,2] += 5
|
||||
System.print(a[0,1,2]) // expect: 5
|
||||
a[0,1,2] -= 1
|
||||
System.print(a[0,1,2]) // expect: 4
|
||||
a[0,1,2] *= 4
|
||||
System.print(a[0,1,2]) // expect: 16
|
||||
a[0,1,2] /= 2
|
||||
System.print(a[0,1,2]) // expect: 8
|
||||
a[0,1,2] <<= 3
|
||||
System.print(a[0,1,2]) // expect: 64
|
||||
a[0,1,2] >>= 2
|
||||
System.print(a[0,1,2]) // expect: 16
|
||||
|
||||
a.other = Compound.new()
|
||||
a.other[0,1,2] = 4
|
||||
System.print(a.other[0,1,2]) // expect: 4
|
||||
a.other[0,1,2] += 10
|
||||
System.print(a.other[0,1,2]) // expect: 14
|
||||
a.other[0,1,2] -= 2
|
||||
System.print(a.other[0,1,2]) // expect: 12
|
||||
a.other[0,1,2] *= 2
|
||||
System.print(a.other[0,1,2]) // expect: 24
|
||||
a.other[0,1,2] /= 6
|
||||
System.print(a.other[0,1,2]) // expect: 4
|
||||
a.other[0,1,2] <<= 6
|
||||
System.print(a.other[0,1,2]) // expect: 256
|
||||
a.other[0,1,2] >>= 3
|
||||
System.print(a.other[0,1,2]) // expect: 32
|
||||
|
||||
a.other.other = Compound.new()
|
||||
a.other.other[1,2,3] = 2
|
||||
System.print(a.other.other[1,2,3]) // expect: 2
|
||||
a.other.other[1,2,3] += 8
|
||||
System.print(a.other.other[1,2,3]) // expect: 10
|
||||
a.other.other[1,2,3] *= 10
|
||||
System.print(a.other.other[1,2,3]) // expect: 100
|
||||
a.other.other[1,2,3] -= 1
|
||||
System.print(a.other.other[1,2,3]) // expect: 99
|
||||
a.other.other[1,2,3] /= 3
|
||||
System.print(a.other.other[1,2,3]) // expect: 33
|
||||
a.other.other[1,2,3] <<= 1
|
||||
System.print(a.other.other[1,2,3]) // expect: 66
|
||||
a.other.other[1,2,3] >>= 4
|
||||
System.print(a.other.other[1,2,3]) // expect: 4
|
||||
|
||||
|
||||
22
test/language/assignment/compound_variable.wren
Normal file
22
test/language/assignment/compound_variable.wren
Normal file
@ -0,0 +1,22 @@
|
||||
var a = 0
|
||||
System.print(a) // expect: 0
|
||||
a += 5
|
||||
System.print(a) // expect: 5
|
||||
a -= 1
|
||||
System.print(a) // expect: 4
|
||||
a *= 4
|
||||
System.print(a) // expect: 16
|
||||
a /= 2
|
||||
System.print(a) // expect: 8
|
||||
a <<= 8
|
||||
System.print(a) // expect: 2048
|
||||
a >>= 6
|
||||
System.print(a) // expect: 32
|
||||
a %= 3
|
||||
System.print(a) // expect: 2
|
||||
a ^= 4
|
||||
System.print(a) // expect: 6
|
||||
a |= 8
|
||||
System.print(a) // expect: 14
|
||||
a &= 6
|
||||
System.print(a) // expect: 6
|
||||
Reference in New Issue
Block a user