Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

New: dictionary literals in Objective-J 2.0.

  • Loading branch information...
commit 5200f945fe905d5f00addaa00bb78ae09a3cedaf 1 parent a736bb4
@aljungberg aljungberg authored
View
29 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:\"");
@Me1000
Me1000 added a note

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

@aljungberg Owner

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.

@Me1000
Me1000 added a note

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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(\"");
View
33 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 <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.
View
10 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;
View
35 Tests/Foundation/CPDictionaryTest.j
@@ -1,5 +1,7 @@
@import <Foundation/CPDictionary.j>
+@import <OJUnit/OJTestCase.j>
+
@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
View
2  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")];

1 comment on commit 5200f94

@Me1000

YAY!!!

@Me1000

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

@aljungberg

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.

@Me1000

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

Please sign in to comment.
Something went wrong with that request. Please try again.