From e740a4c95a67f7e967aceb7bfb8f7a2197d899eb Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sat, 24 Jan 2015 23:21:50 -0800 Subject: [PATCH] Map literals! --- src/wren_compiler.c | 56 +++++++++++++++++++++++++++++++++-- src/wren_debug.c | 1 + src/wren_vm.c | 3 ++ src/wren_vm.h | 3 ++ test/map/count.wren | 2 +- test/map/eof_after_colon.wren | 1 + test/map/eof_after_comma.wren | 2 ++ test/map/eof_after_key.wren | 1 + test/map/eof_after_value.wren | 2 ++ test/map/grow.wren | 2 +- test/map/key_types.wren | 20 ++++++------- test/map/type.wren | 10 +++---- 12 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 test/map/eof_after_colon.wren create mode 100644 test/map/eof_after_comma.wren create mode 100644 test/map/eof_after_key.wren create mode 100644 test/map/eof_after_value.wren diff --git a/src/wren_compiler.c b/src/wren_compiler.c index 78297493..268d4a90 100644 --- a/src/wren_compiler.c +++ b/src/wren_compiler.c @@ -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: diff --git a/src/wren_debug.c b/src/wren_debug.c index ce32f1ae..ae03d3e4 100644 --- a/src/wren_debug.c +++ b/src/wren_debug.c @@ -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: diff --git a/src/wren_vm.c b/src/wren_vm.c index 30252607..316302e8 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -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(); diff --git a/src/wren_vm.h b/src/wren_vm.h index f15195f1..80c5d3f9 100644 --- a/src/wren_vm.h +++ b/src/wren_vm.h @@ -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, diff --git a/test/map/count.wren b/test/map/count.wren index 2c034d42..028258fe 100644 --- a/test/map/count.wren +++ b/test/map/count.wren @@ -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 diff --git a/test/map/eof_after_colon.wren b/test/map/eof_after_colon.wren new file mode 100644 index 00000000..d331f56b --- /dev/null +++ b/test/map/eof_after_colon.wren @@ -0,0 +1 @@ +var map = {1: // expect error \ No newline at end of file diff --git a/test/map/eof_after_comma.wren b/test/map/eof_after_comma.wren new file mode 100644 index 00000000..62d7ddd9 --- /dev/null +++ b/test/map/eof_after_comma.wren @@ -0,0 +1,2 @@ +var map = {1: 2, +// expect error \ No newline at end of file diff --git a/test/map/eof_after_key.wren b/test/map/eof_after_key.wren new file mode 100644 index 00000000..c2f7213f --- /dev/null +++ b/test/map/eof_after_key.wren @@ -0,0 +1 @@ +var map = {1 // expect error \ No newline at end of file diff --git a/test/map/eof_after_value.wren b/test/map/eof_after_value.wren new file mode 100644 index 00000000..c28e5b2c --- /dev/null +++ b/test/map/eof_after_value.wren @@ -0,0 +1,2 @@ +var map = {1: 2 +// expect error \ No newline at end of file diff --git a/test/map/grow.wren b/test/map/grow.wren index f1b3d54d..da1c2d63 100644 --- a/test/map/grow.wren +++ b/test/map/grow.wren @@ -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 } diff --git a/test/map/key_types.wren b/test/map/key_types.wren index c91bdf45..31054b6f 100644 --- a/test/map/key_types.wren +++ b/test/map/key_types.wren @@ -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 diff --git a/test/map/type.wren b/test/map/type.wren index fb808e3c..11e4913b 100644 --- a/test/map/type.wren +++ b/test/map/type.wren @@ -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