diff --git a/Objective-J/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index 7e6faa6f53..664cfe3711 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -774,6 +774,35 @@ Identifier: function(node, st, c) { } } }, +DictionaryLiteral: function(node, st, c) { + CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start)); + st.compiler.lastPos = node.start; + + if (!node.keys.length) { + CONCAT(st.compiler.jsBuffer, "objj_msgSend(objj_msgSend(CPDictionary, \"alloc\"), \"init\")"); + } else { + CONCAT(st.compiler.jsBuffer, "objj_msgSend(objj_msgSend(CPDictionary, \"alloc\"), \"initWithObjectsAndKeys:\""); + for (var i = 0; i < node.keys.length; i++) { + var key = node.keys[i], + value = node.values[i]; + + CONCAT(st.compiler.jsBuffer, ", "); + + st.compiler.lastPos = value.start; + c(value, st, "Expression"); + CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, value.end)); + + CONCAT(st.compiler.jsBuffer, ", "); + + st.compiler.lastPos = key.start; + c(key, st, "Expression"); + CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, key.end)); + } + CONCAT(st.compiler.jsBuffer, ")"); + } + + st.compiler.lastPos = node.end; +}, SelectorLiteralExpression: function(node, st, c) { CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start)); CONCAT(st.compiler.jsBuffer, "sel_getUid(\""); diff --git a/Objective-J/acorn.js b/Objective-J/acorn.js index a0b1f073ae..826b7af2e6 100644 --- a/Objective-J/acorn.js +++ b/Objective-J/acorn.js @@ -273,6 +273,7 @@ if (!exports.acorn) { var _implementation = {keyword: "implementation"}, _outlet = {keyword: "outlet"}, _accessors = {keyword: "accessors"}; var _end = {keyword: "end"}, _import = {keyword: "import", afterImport: true}; var _action = {keyword: "action"}, _selector = {keyword: "selector"}, _class = {keyword: "class"}, _global = {keyword: "global"}; + var _dictionaryLiteral = {keyword: "{"}; // Objective-J keywords @@ -660,6 +661,9 @@ if (!exports.acorn) { var next = input.charCodeAt(++tokPos); if (next === 34 || next === 39) // Read string if "'" or '"' return readString(next); + if (next === 123) // Read dictionary literal if "{" + return finishToken(_dictionaryLiteral); + var word = readWord1(), token = objJAtKeywordTypes[word]; if (!token) raise(tokStart, "Unrecognized Objective-J keyword '@" + word + "'"); @@ -1878,6 +1882,15 @@ if (!exports.acorn) { node.elements = parseExprList(_bracketR, firstExpr, true, true); return finishNode(node, "ArrayExpression"); + case _dictionaryLiteral: + var node = startNode(); + next(); + + var r = parseDictionary(); + node.keys = r[0]; + node.values = r[1]; + return finishNode(node, "DictionaryLiteral"); + case _braceL: return parseObj(); @@ -2088,6 +2101,26 @@ if (!exports.acorn) { return elts; } + // Parses a comma-separated list of : pairs and returns them as + // [arrayOfKeyExpressions, arrayOfValueExpressions]. + function parseDictionary() { + expect(_braceL, "Expected '{' before dictionary"); + + var keys = [], values = [], first = true; + while (!eat(_braceR)) { + if (!first) { + expect(_comma, "Expected ',' between expressions"); + if (options.allowTrailingCommas && eat(_braceR)) break; + } + + keys.push(parseExpression(true, true)); + expect(_colon, "Expected ':' between dictionary key and value"); + values.push(parseExpression(true, true)); + first = false; + } + return [keys, values]; + } + // Parse the next token as an identifier. If `liberal` is true (used // when parsing properties), it will also convert keywords into // identifiers. diff --git a/Objective-J/acornwalk.js b/Objective-J/acornwalk.js index ca2295fbd5..7e59ba0679 100644 --- a/Objective-J/acornwalk.js +++ b/Objective-J/acornwalk.js @@ -155,6 +155,14 @@ if (!exports.acorn) { if (elt) c(elt, st, "Expression"); } }; + exports.DictionaryLiteral = function(node, st, c) { + for (var i = 0; i < node.keys.length; i++) { + var key = node.keys[i]; + c(key, st, "Expression"); + var value = node.values[i]; + c(value, st, "Expression"); + } + }; exports.ObjectExpression = function(node, st, c) { for (var i = 0; i < node.properties.length; ++i) c(node.properties[i].value, st, "Expression"); @@ -195,7 +203,7 @@ if (!exports.acorn) { c(node.body[i], st, "Statement"); } } - + exports.ImportStatement = ignore; exports.IvarDeclaration = ignore; diff --git a/Tests/Foundation/CPDictionaryTest.j b/Tests/Foundation/CPDictionaryTest.j index a810aefbc1..f8682ffa02 100644 --- a/Tests/Foundation/CPDictionaryTest.j +++ b/Tests/Foundation/CPDictionaryTest.j @@ -1,5 +1,7 @@ @import +@import + @implementation CPDictionaryTest : OJTestCase { CPDictionary string_dict; @@ -403,9 +405,40 @@ { var dict = [[CPDictionary alloc] initWithObjectsAndKeys:@"Value1", @"Key1", nil, @"Key2", @"Value3", @"Key3"]; - [self assert:2 equals:[dict count]]; + [self assert:2 equals:[dict count]]; [self assert:@"Value1" equals:[dict objectForKey:@"Key1"]]; [self assert:nil equals:[dict objectForKey:@"Key2"]]; // No key/value pair [self assert:@"Value3" equals:[dict objectForKey:@"Key3"]]; } + +- (void)testDictionaryLiteral +{ + var dict = @{ + @"Key1": @"Value1", + @"Key2": [CPNull null], + @"Key3": 2 + }; + + [self assert:3 equals:[dict count]]; + [self assert:@"Value1" equals:[dict objectForKey:@"Key1"]]; + [self assert:[CPNull null] same:[dict objectForKey:@"Key2"]]; + [self assert:2 equals:[dict objectForKey:@"Key3"]]; +} + +- (void)testDictionaryLiteralExpressions +{ + var aKey = @"aKey", + aValue = 5, + dict = @{ + @"Key" + 1: @"Value" + 1, + @"Key2": NO ? 1 : 2, + aKey: aValue, // trailing comma is allowed + }; + + [self assert:3 equals:[dict count]]; + [self assert:@"Value1" equals:[dict objectForKey:@"Key1"]]; + [self assert:2 equals:[dict objectForKey:@"Key2"]]; + [self assert:5 equals:[dict objectForKey:@"aKey"]]; +} + @end diff --git a/Tests/Objective-J/CFDictionaryTest.j b/Tests/Objective-J/CFDictionaryTest.j index 924d209fef..946022f012 100644 --- a/Tests/Objective-J/CFDictionaryTest.j +++ b/Tests/Objective-J/CFDictionaryTest.j @@ -20,7 +20,7 @@ { var dict = new CFMutableDictionary(); [self assert:0 equals:dict.countOfValue(@"123")]; - + dict.setValueForKey(@"abc", @"123"); [self assert:1 equals:dict.countOfValue(@"123")];