10 Commits

10 changed files with 488 additions and 30 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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)

View File

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

View 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

View 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

View 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

View 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

View 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

View 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