Map literals!

This commit is contained in:
Bob Nystrom
2015-01-24 23:21:50 -08:00
parent abe80e6d4b
commit e740a4c95a
12 changed files with 83 additions and 20 deletions

View File

@ -1315,7 +1315,8 @@ typedef enum
PREC_TERM, // + -
PREC_FACTOR, // * / %
PREC_UNARY, // unary - ! ~
PREC_CALL // . () []
PREC_CALL, // . () []
PREC_PRIMARY
} Precedence;
typedef void (*GrammarFn)(Compiler*, bool allowAssignment);
@ -1574,12 +1575,14 @@ static void loadThis(Compiler* compiler)
}
}
// A parenthesized expression.
static void grouping(Compiler* compiler, bool allowAssignment)
{
expression(compiler);
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
}
// A list literal.
static void list(Compiler* compiler, bool allowAssignment)
{
// Compile the list elements.
@ -1603,6 +1606,54 @@ static void list(Compiler* compiler, bool allowAssignment)
emitByteArg(compiler, CODE_LIST, numElements);
}
// A map literal.
static void map(Compiler* compiler, bool allowAssignment)
{
// TODO: Do we want to do the same thing for list literals and remove the
// bytecodes for them?
// Load the Map class.
int mapClassSymbol = wrenSymbolTableFind(&compiler->parser->vm->globalNames,
"Map", 3);
ASSERT(mapClassSymbol != -1, "Should have already defined 'Map' global.");
emitShortArg(compiler, CODE_LOAD_GLOBAL, mapClassSymbol);
// Instantiate a new map.
emitShortArg(compiler, CODE_CALL_0,
methodSymbol(compiler, " instantiate", 12));
int subscriptSetSymbol = methodSymbol(compiler, "[ ]=", 4);
// Compile the map elements. Each one is compiled to just invoke the
// subscript setter on the map.
if (peek(compiler) != TOKEN_RIGHT_BRACE)
{
do
{
ignoreNewlines(compiler);
// Push a copy of the map since the subscript call will consume it.
emit(compiler, CODE_DUP);
// The key.
parsePrecedence(compiler, false, PREC_PRIMARY);
consume(compiler, TOKEN_COLON, "Expect ':' after map key.");
// The value.
expression(compiler);
emitShortArg(compiler, CODE_CALL_2, subscriptSetSymbol);
// Discard the result of the setter call.
emit(compiler, CODE_POP);
} while (match(compiler, TOKEN_COMMA));
}
// Allow newlines before the closing '}'.
ignoreNewlines(compiler);
consume(compiler, TOKEN_RIGHT_BRACE, "Expect '}' after map entries.");
}
// Unary operators like `-foo`.
static void unaryOp(Compiler* compiler, bool allowAssignment)
{
@ -2174,7 +2225,7 @@ GrammarRule rules[] =
/* TOKEN_RIGHT_PAREN */ UNUSED,
/* TOKEN_LEFT_BRACKET */ { list, subscript, subscriptSignature, PREC_CALL, NULL },
/* TOKEN_RIGHT_BRACKET */ UNUSED,
/* TOKEN_LEFT_BRACE */ UNUSED,
/* TOKEN_LEFT_BRACE */ PREFIX(map),
/* TOKEN_RIGHT_BRACE */ UNUSED,
/* TOKEN_COLON */ UNUSED,
/* TOKEN_DOT */ INFIX(PREC_CALL, call),
@ -2297,6 +2348,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_FALSE:
case CODE_TRUE:
case CODE_POP:
case CODE_DUP:
case CODE_IS:
case CODE_CLOSE_UPVALUE:
case CODE_RETURN:

View File

@ -110,6 +110,7 @@ static int debugPrintInstruction(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_DUP: printf("DUP\n"); break;
case CODE_CALL_0:
case CODE_CALL_1:

View File

@ -450,6 +450,7 @@ static bool runInterpreter(WrenVM* vm)
&&code_LOAD_FIELD,
&&code_STORE_FIELD,
&&code_POP,
&&code_DUP,
&&code_CALL_0,
&&code_CALL_1,
&&code_CALL_2,
@ -559,6 +560,8 @@ static bool runInterpreter(WrenVM* vm)
}
CASE_CODE(POP): DROP(); DISPATCH();
CASE_CODE(DUP): PUSH(PEEK()); DISPATCH();
CASE_CODE(NULL): PUSH(NULL_VAL); DISPATCH();
CASE_CODE(FALSE): PUSH(FALSE_VAL); DISPATCH();
CASE_CODE(TRUE): PUSH(TRUE_VAL); DISPATCH();

View File

@ -77,6 +77,9 @@ typedef enum
// Pop and discard the top of stack.
CODE_POP,
// Push a copy of the value currently on the top of the stack.
CODE_DUP,
// Invoke the method with symbol [arg]. The number indicates the number of
// arguments (not including the receiver).
CODE_CALL_0,

View File

@ -1,4 +1,4 @@
var map = new Map
var map = {}
IO.print(map.count) // expect: 0
map["one"] = "value"
IO.print(map.count) // expect: 1

View File

@ -0,0 +1 @@
var map = {1: // expect error

View File

@ -0,0 +1,2 @@
var map = {1: 2,
// expect error

View File

@ -0,0 +1 @@
var map = {1 // expect error

View File

@ -0,0 +1,2 @@
var map = {1: 2
// expect error

View File

@ -58,7 +58,7 @@ var fishes = [
"Cutlassfish", "Cutthroat eel", "Cutthroat trout"
]
var map = new Map
var map = {}
for (fish in fishes) {
map[fish] = fish.count
}

View File

@ -1,13 +1,13 @@
var map = new Map
map[null] = "null value"
map[true] = "true value"
map[false] = "false value"
map[0] = "zero"
map[1.2] = "1 point 2"
map[List] = "list class"
map["null"] = "string value"
map[1..3] = "1 to 3"
var map = {
null: "null value",
true: "true value",
false: "false value",
0: "zero",
1.2: "1 point 2",
List: "list class",
"null": "string value",
(1..3): "1 to 3"
}
IO.print(map[null]) // expect: null value
IO.print(map[true]) // expect: true value

View File

@ -1,7 +1,5 @@
// TODO: Use map literal.
IO.print(new Map is Map) // expect: true
IO.print({} is Map) // expect: true
// TODO: Abstract base class for associations.
IO.print(new Map is Object) // expect: true
IO.print(new Map is Bool) // expect: false
IO.print((new Map).type == Map) // expect: true
IO.print({} is Object) // expect: true
IO.print({} is Bool) // expect: false
IO.print({}.type == Map) // expect: true