Skip to content

Commit

Permalink
New: dictionary literals in Objective-J 2.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
aljungberg committed Feb 25, 2013
1 parent a736bb4 commit 5200f94
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 3 deletions.
29 changes: 29 additions & 0 deletions Objective-J/ObjJAcornCompiler.js
Expand Up @@ -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:\"");

This comment has been minimized.

Copy link
@Me1000

Me1000 Feb 25, 2013

Contributor

Do you think, perhaps, we should be using CFDictionaries here (since those exist at the language level) instead of CPDictionaries?

This comment has been minimized.

Copy link
@aljungberg

aljungberg Feb 25, 2013

Author Member

This is closer to what Objective-C does: http://clang.llvm.org/docs/ObjectiveCLiterals.html. It's not exactly right because Objective-C actually calls +[NSDictionary dictionaryWithObjects:forKeys:count:], but I wanted to save that one method call, while still leaving it open for people who replace parts of CPDictionary using categories or whatever.

This comment has been minimized.

Copy link
@Me1000

Me1000 Feb 25, 2013

Contributor

Hmmm, interesting... That seems like an odd choice on their part, but I guess they gave their reasons.

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(\"");
Expand Down
33 changes: 33 additions & 0 deletions Objective-J/acorn.js
Expand Up @@ -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

Expand Down Expand Up @@ -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 + "'");
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -2088,6 +2101,26 @@ if (!exports.acorn) {
return elts;
}

// Parses a comma-separated list of <key>:<value> 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.
Expand Down
10 changes: 9 additions & 1 deletion Objective-J/acornwalk.js
Expand Up @@ -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");
Expand Down Expand Up @@ -195,7 +203,7 @@ if (!exports.acorn) {
c(node.body[i], st, "Statement");
}
}

exports.ImportStatement = ignore;

exports.IvarDeclaration = ignore;
Expand Down
35 changes: 34 additions & 1 deletion Tests/Foundation/CPDictionaryTest.j
@@ -1,5 +1,7 @@
@import <Foundation/CPDictionary.j>

@import <OJUnit/OJTestCase.j>

@implementation CPDictionaryTest : OJTestCase
{
CPDictionary string_dict;
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion Tests/Objective-J/CFDictionaryTest.j
Expand Up @@ -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")];

Expand Down

1 comment on commit 5200f94

@Me1000
Copy link
Contributor

@Me1000 Me1000 commented on 5200f94 Feb 25, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YAY!!!

Please sign in to comment.