1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | #import "RCTParserUtils.h"
|
9 |
|
10 | #import "RCTLog.h"
|
11 |
|
12 | @implementation RCTParserUtils
|
13 |
|
14 | BOOL RCTReadChar(const char **input, char c)
|
15 | {
|
16 | if (**input == c) {
|
17 | (*input)++;
|
18 | return YES;
|
19 | }
|
20 | return NO;
|
21 | }
|
22 |
|
23 | BOOL RCTReadString(const char **input, const char *string)
|
24 | {
|
25 | int i;
|
26 | for (i = 0; string[i] != 0; i++) {
|
27 | if (string[i] != (*input)[i]) {
|
28 | return NO;
|
29 | }
|
30 | }
|
31 | *input += i;
|
32 | return YES;
|
33 | }
|
34 |
|
35 | void RCTSkipWhitespace(const char **input)
|
36 | {
|
37 | while (isspace(**input)) {
|
38 | (*input)++;
|
39 | }
|
40 | }
|
41 |
|
42 | static BOOL RCTIsIdentifierHead(const char c)
|
43 | {
|
44 | return isalpha(c) || c == '_';
|
45 | }
|
46 |
|
47 | static BOOL RCTIsIdentifierTail(const char c)
|
48 | {
|
49 | return isalnum(c) || c == '_';
|
50 | }
|
51 |
|
52 | BOOL RCTParseArgumentIdentifier(const char **input, NSString **string)
|
53 | {
|
54 | const char *start = *input;
|
55 |
|
56 | do {
|
57 | if (!RCTIsIdentifierHead(**input)) {
|
58 | return NO;
|
59 | }
|
60 | (*input)++;
|
61 |
|
62 | while (RCTIsIdentifierTail(**input)) {
|
63 | (*input)++;
|
64 | }
|
65 |
|
66 | // allow namespace resolution operator
|
67 | } while (RCTReadString(input, "::"));
|
68 |
|
69 | if (string) {
|
70 | *string = [[NSString alloc] initWithBytes:start
|
71 | length:(NSInteger)(*input - start)
|
72 | encoding:NSASCIIStringEncoding];
|
73 | }
|
74 | return YES;
|
75 | }
|
76 |
|
77 | BOOL RCTParseSelectorIdentifier(const char **input, NSString **string)
|
78 | {
|
79 | const char *start = *input;
|
80 | if (!RCTIsIdentifierHead(**input)) {
|
81 | return NO;
|
82 | }
|
83 | (*input)++;
|
84 | while (RCTIsIdentifierTail(**input)) {
|
85 | (*input)++;
|
86 | }
|
87 | if (string) {
|
88 | *string = [[NSString alloc] initWithBytes:start
|
89 | length:(NSInteger)(*input - start)
|
90 | encoding:NSASCIIStringEncoding];
|
91 | }
|
92 | return YES;
|
93 | }
|
94 |
|
95 | static BOOL RCTIsCollectionType(NSString *type)
|
96 | {
|
97 | static NSSet *collectionTypes;
|
98 | static dispatch_once_t onceToken;
|
99 | dispatch_once(&onceToken, ^{
|
100 | collectionTypes = [[NSSet alloc] initWithObjects:
|
101 | @"NSArray", @"NSSet", @"NSDictionary", nil];
|
102 | });
|
103 | return [collectionTypes containsObject:type];
|
104 | }
|
105 |
|
106 | NSString *RCTParseType(const char **input)
|
107 | {
|
108 | NSString *type;
|
109 | RCTParseArgumentIdentifier(input, &type);
|
110 | RCTSkipWhitespace(input);
|
111 | if (RCTReadChar(input, '<')) {
|
112 | RCTSkipWhitespace(input);
|
113 | NSString *subtype = RCTParseType(input);
|
114 | if (RCTIsCollectionType(type)) {
|
115 | if ([type isEqualToString:@"NSDictionary"]) {
|
116 | // Dictionaries have both a key *and* value type, but the key type has
|
117 | // to be a string for JSON, so we only care about the value type
|
118 | if (RCT_DEBUG && ![subtype isEqualToString:@"NSString"]) {
|
119 | RCTLogError(@"%@ is not a valid key type for a JSON dictionary", subtype);
|
120 | }
|
121 | RCTSkipWhitespace(input);
|
122 | RCTReadChar(input, ',');
|
123 | RCTSkipWhitespace(input);
|
124 | subtype = RCTParseType(input);
|
125 | }
|
126 | if (![subtype isEqualToString:@"id"]) {
|
127 | type = [type stringByReplacingCharactersInRange:(NSRange){0, 2 }
|
128 | withString:subtype];
|
129 | }
|
130 | } else {
|
131 | // It's a protocol rather than a generic collection - ignore it
|
132 | }
|
133 | RCTSkipWhitespace(input);
|
134 | RCTReadChar(input, '>');
|
135 | }
|
136 | RCTSkipWhitespace(input);
|
137 | if (!RCTReadChar(input, '*')) {
|
138 | RCTReadChar(input, '&');
|
139 | }
|
140 | return type;
|
141 | }
|
142 |
|
143 | @end
|